home *** CD-ROM | disk | FTP | other *** search
- /*
- * (c) Copyright 1992 by Panagiotis Tsirigotis
- * All rights reserved. The file named COPYRIGHT specifies the terms
- * and conditions for redistribution.
- */
-
- static char RCSid[] = "$Id: reconfig.c,v 5.2 1992/11/10 08:18:25 panos Exp $" ;
-
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <syslog.h>
- #include <signal.h>
- #include <memory.h>
-
- #include "access.h"
- #include "defs.h"
- #include "state.h"
- #include "service.h"
- #include "server.h"
- #include "conf.h"
-
- void exit() ;
-
- void msg() ;
- void out_of_memory() ;
-
- #define RECONFIG_SOFT 1
- #define RECONFIG_HARD 2
-
- #define SWAP( x, y, temp ) (temp) = (x), (x) = (y), (y) = (temp)
-
-
- PRIVATE void do_reconfig() ;
-
-
- void soft_reconfig()
- {
- do_reconfig( RECONFIG_SOFT ) ;
- }
-
-
- void hard_reconfig()
- {
- do_reconfig( RECONFIG_HARD ) ;
- }
-
-
- /*
- * Reconfigure the server by rereading the configuration file.
- * Services may be added, deleted or have their attributes changed.
- * All syslog output uses the LOG_NOTICE priority level (except for
- * errors).
- */
- PRIVATE void do_reconfig( reconfig_type )
- int reconfig_type ;
- {
- struct service *osp, *nsp ;
- struct configuration new_conf ;
- psi_h iter ;
- unsigned old_services = 0 ;
- unsigned new_services ;
- unsigned dropped_services = 0 ;
- char *func = "do_reconfig" ;
- status_e readjust() ;
- void terminate_servers() ;
- void cancel_service_retries() ;
- void destroy_services() ;
- void check_servers() ;
- unsigned start_services() ;
- void swap_defaults() ;
- struct service *service_lookup() ;
-
- msg( LOG_NOTICE, func, "Starting %s reconfiguration",
- ( reconfig_type == RECONFIG_SOFT ) ? "soft" : "hard" ) ;
-
- if ( get_configuration( &new_conf ) == FAILED )
- {
- msg( LOG_WARNING, func, "reconfiguration failed" ) ;
- return ;
- }
-
- iter = psi_create( SERVICES( ps ) ) ;
- if ( iter == NULL )
- {
- out_of_memory( func ) ;
- free_configuration( &new_conf ) ;
- return ;
- }
-
- swap_defaults( &new_conf ) ;
-
- /*
- * There are 3 possibilities for a service:
- *
- * A) it is in the current service table but not in the new one
- * B) it is in the new service table but not in the current one
- * C) it is both in the current service table and the new one
- *
- * Services of type A are dropped from the active services. Their
- * running servers are either left alone (soft reconfiguration) or
- * they are terminated (hard reconfiguration).
- *
- * Services of type B are added to the active services.
- *
- * Services of type C have some of their attributes readjusted.
- */
-
- /*
- * Step 1: find the services NOT in the new service table
- */
- for ( osp = SP( psi_start( iter ) ) ; osp ; osp = SP( psi_next( iter ) ) )
- {
- char *sid = CONF( osp )->id ;
- boolean_e drop_service ;
-
- if ( ! IS_AVAILABLE( osp ) )
- continue ;
-
- if ( ( nsp = service_lookup( new_conf.services, osp ) ) == NULL ||
- fundamental_change( CONF( osp ), CONF( nsp ) ) )
- {
- /*
- * Procedure for disabling a service:
- *
- * a. Terminate running servers/cancel retry attempts, in case
- * of hard reconfiguration
- * b. Clear the fd of the service from the socket_mask
- * c. Delete the service from the service table, if possible
- */
-
- if ( reconfig_type == RECONFIG_HARD )
- {
- terminate_servers( osp ) ;
- cancel_service_retries( osp ) ;
- }
- drop_service = YES ;
- }
- else
- {
- /*
- * Readjust service
- */
- if ( readjust( osp, CONF( nsp ), reconfig_type ) == OK )
- {
- /*
- * Do the access control again
- */
- if ( reconfig_type == RECONFIG_HARD )
- check_servers( osp ) ;
-
- /*
- * Change the state so we know which ones among the new services
- * were readjusted and which ones are new
- */
- nsp->state = SVC_DISABLED ;
- old_services++ ;
- drop_service = NO ;
- }
- else
- {
- /*
- * The readjustment failed. If this is a hard reconfiguration,
- * cancel retries and terminate servers.
- */
- if ( reconfig_type == RECONFIG_HARD )
- {
- cancel_service_retries( osp ) ;
- terminate_servers( osp ) ;
- }
- drop_service = YES ;
- }
- }
-
- if ( drop_service == YES )
- {
- /*
- * The service will be deleted only if it is not being referenced
- * In either case, it is deactivated
- */
- svc_deactivate( osp ) ;
- msg( LOG_NOTICE, func, "service %s deactivated", sid ) ;
- if ( SVC_RELE( osp ) == 0 )
- psi_remove( iter ) ;
- dropped_services++ ;
- }
- }
-
- /*
- * Step 2: Add new services to the service table
- *
- * At this point, all services in the new_services table are split in
- * two groups depending on their state: SVC_NOT_STARTED and SVC_DISABLED
- * The start_services function will activate those whose state is
- * SVC_NOT_STARTED
- */
-
- new_services = start_services( new_conf.services ) ;
-
- msg( LOG_NOTICE, func,
- "Reconfigured: new=%d old=%d dropped=%d (services)",
- new_services, old_services, dropped_services ) ;
-
- if ( ps.rws.available_services == 0 )
- {
- msg( LOG_CRIT, func, "No available services. Exiting" ) ;
- exit( 1 ) ;
- }
-
- free_configuration( &new_conf ) ;
- }
-
-
- PRIVATE void swap_defaults( confp )
- struct configuration *confp ;
- {
- struct service *temp ;
- void end_log() ;
-
- /*
- * Close the previous common log file, if one was specified
- */
- if ( DEFAULT_LOG( ps ) != NULL )
- {
- end_log( LOG( DEFAULTS( ps ) )->log_type, DEFAULT_LOG( ps ) ) ;
- DEFAULT_LOG( ps ) = NULL ;
- }
- DEFAULT_LOG_ERROR( ps ) = FALSE ;
-
- /*
- * We set the reference count of the old defaults to 0, so that
- * it will be free'd when the configuration is free'd
- * Setting the reference count of the new defaults to 1 is done
- * for aesthetic reasons.
- */
- confp->defaults->ref_count = 1 ;
- ps.rws.cs.defaults->ref_count = 0 ;
-
- SWAP( ps.rws.cs.defaults, confp->defaults, temp ) ;
- }
-
-
- /*
- * This functions returns TRUE if there has been a change in the values of
- * the following attributes:
- * wait, type, socket_type, protocol.value
- */
- PRIVATE bool_int fundamental_change( old_conf, new_conf )
- struct service_config *old_conf ;
- struct service_config *new_conf ;
- {
- if ( IS_UNLISTED( old_conf ) != IS_UNLISTED( new_conf ) ||
- IS_INTERNAL( old_conf ) != IS_INTERNAL( new_conf ) )
- return( TRUE ) ;
-
- if ( old_conf->wait != new_conf->wait )
- return( TRUE ) ;
-
- if ( IS_RPC( old_conf ) )
- {
- if ( old_conf->protocol.value != new_conf->protocol.value )
- return( TRUE ) ;
- }
- else
- {
- if ( old_conf->socket_type != new_conf->socket_type ||
- old_conf->protocol.value != new_conf->protocol.value )
- return( TRUE ) ;
-
- if ( IS_UNLISTED( old_conf ) && old_conf->port != new_conf->port )
- return( TRUE ) ;
- }
- return( FALSE ) ;
- }
-
-
-
- /*
- * Two RPC services are considered the same if they have the same program
- * number.
- * Two non-RPC services are considered the same if they use the same
- * protocol,port pair
- */
- #define SAME_RPC( s1, s2 ) \
- ( RPCDATA( s1 )->program_number == RPCDATA( s2 )->program_number )
- #define SAME_NONRPC( s1, s2 ) \
- ( (s1)->socket_type == (s2)->socket_type && (s1)->port == (s2)->port )
-
- PRIVATE struct service *service_lookup( stab, sp )
- pset_h stab ;
- struct service *sp ;
- {
- register struct service_config *scp = CONF( sp ) ;
- register int is_rpc_service = IS_RPC( scp ) ;
- register unsigned u ;
-
- for ( u = 0 ; u < pset_count( stab ) ; u++ )
- {
- struct service *isp = SP( pset_pointer( stab, u ) ) ;
- register struct service_config *iscp = CONF( isp ) ;
-
- if ( ! EQ( scp->id, iscp->id ) )
- continue ;
-
- /*
- * Two services are the same if:
- *
- * they are RPC services and they have the same program number
- * they are not RPC and they use the same protocol,port pair
- */
- if ( is_rpc_service && IS_RPC( iscp ) && SAME_RPC( scp, iscp ) ||
- ! is_rpc_service && ! IS_RPC( iscp ) && SAME_NONRPC( scp, iscp ) )
- return( isp ) ;
- }
- return( NULL ) ;
- }
-
-
-
- PRIVATE void sendsig( serp, sig )
- register struct server *serp ;
- int sig ;
- {
- struct service *sp = SERVER_SERVICE( serp ) ;
- char *func = "sendsig" ;
-
- /*
- * Always use a positive pid, because of the semantics of kill(2)
- */
- if ( serp->pid > 0 )
- {
- msg( LOG_WARNING, func, "Sending signal %d to %s server %d",
- sig, CONF( sp )->id, serp->pid ) ;
- (void) kill( serp->pid, sig ) ;
- }
- else
- msg( LOG_ERR, func,
- "Negative server pid = %d. Service %s", serp->pid, CONF( sp )->id ) ;
- }
-
-
- /*
- * Send signal sig to all running servers of service sp
- */
- PRIVATE void deliver_signal( sp, sig )
- register struct service *sp ;
- int sig ;
- {
- register unsigned u ;
-
- for ( u = 0 ; u < pset_count( ps.rws.servers ) ; u++ )
- {
- register struct server *serp ;
-
- serp = SERP( pset_pointer( ps.rws.servers, u ) ) ;
- if ( SERVER_SERVICE( serp ) == sp )
- sendsig( serp, sig ) ;
- }
- }
-
-
- /*
- * Terminate all servers of the specified service
- */
- void terminate_servers( sp )
- register struct service *sp ;
- {
- int sig = IS_INTERNAL( CONF( sp ) ) ? SIGTERM : SIGKILL ;
-
- deliver_signal( sp, sig ) ;
- }
-
-
- PRIVATE void stop_interception( sp )
- struct service *sp ;
- {
- deliver_signal( sp, INTERCEPT_SIG ) ;
- }
-
-
- PRIVATE char *termination_reason( res )
- access_e res ;
- {
- if ( res == AC_TIME )
- return( "time" ) ;
- else if ( res == AC_ADDRESS )
- return( "address" ) ;
- else if ( res == AC_SERVICE_LIMIT )
- return( "service instances" ) ;
- else
- return( "UNKNOWN" ) ;
- }
-
-
-
- /*
- * Do access control on all servers of the specified service.
- * If "limit" is 0, we don't mind if the service limit is exceeded.
- * If "limit" is non-zero, we kill servers that exceed the service limit
- * (but at most "limit" servers are killed because of exceeding the
- * instances limit)
- *
- * Return value: number of servers that failed the check (and were killed)
- */
- PRIVATE int server_check( sp, limit )
- register struct service *sp ;
- int limit ;
- {
- register unsigned u ;
- int killed_servers = 0 ;
- pset_h server_set = ps.rws.servers ;
- char *func = "server_check" ;
- access_e access_control() ;
-
- if ( SDATA( sp )->running_servers == 0 )
- return( 0 ) ;
-
- for ( u = 0 ; u < pset_count( server_set ) ; u++ )
- {
- register struct server *serp = SERP( pset_pointer( server_set, u ) ) ;
-
- if ( SERVER_SERVICE( serp ) == sp )
- {
- access_e result ;
- mask_t check_mask = MASK( CF_TIME ) ;
-
- if ( CONF( sp )->socket_type != SOCK_DGRAM )
- M_OR( check_mask, check_mask, MASK( CF_ADDRESS ) ) ;
- if ( limit != 0 && killed_servers < limit )
- M_OR( check_mask, check_mask, MASK( CF_SERVICE_LIMIT ) ) ;
-
- result = access_control( sp, SERVER_CONNECTION( serp ), &check_mask );
-
- if ( result == AC_OK )
- continue ;
-
- msg( LOG_NOTICE, func, "%s server %d failed %s check",
- CONF( sp )->id, serp->pid, termination_reason( result ) ) ;
- sendsig( serp, SIGKILL ) ;
- killed_servers++ ;
- }
- }
- return( killed_servers ) ;
- }
-
-
- /*
- * Check if all the running servers of the specified service fit the
- * new access control specifications. The ones that don't fit are killed.
- *
- * We go through the server table twice. During the first
- * pass we ignore overruns of the server limit. During the second pass
- * we don't ignore such overruns until a certain number of servers are
- * terminated.
- */
- PRIVATE void check_servers( sp )
- register struct service *sp ;
- {
- int existant_servers = SDATA( sp )->running_servers ;
- int limit ;
-
- existant_servers -= server_check( sp, 0 ) ;
-
- limit = existant_servers - CONF( sp )->instances ;
- if ( limit < 0 )
- limit = 0 ;
- (void) server_check( sp, limit ) ;
- }
-
-
-
- /*
- * Readjust service attributes.
- *
- * We assume that the following attributes are the same:
- * wait
- * socket_type
- * type
- * protocol
- *
- * Readjustment happens in 3 steps:
- * 1) We swap the service.conf fields
- * This has the side-effect of free'ing the memory associated
- * with the old service configuration when the new configuration
- * is destroyed.
- * 2) We readjust the fields that require some action to be taken:
- * RPC mapping
- * log file open
- * 3) We update the service_data
- */
- PRIVATE status_e readjust( sp, new_conf, type )
- register struct service *sp ;
- register struct service_config *new_conf ;
- int type ;
- {
- struct service_config *old_conf ;
- struct service_config temp_conf ;
- char *sid = CONF( sp )->id ;
- char *func = "readjust" ;
- status_e readjust_rpc_service() ;
- status_e restart_log() ;
-
- msg( LOG_NOTICE, func, "readjusting service %s", sid ) ;
-
- SWAP( *CONF( sp ), *new_conf, temp_conf ) ;
- old_conf = new_conf ;
- new_conf = CONF( sp ) ;
-
- if ( IS_RPC( old_conf ) &&
- readjust_rpc_service( old_conf, new_conf ) == FAILED )
- return( FAILED ) ;
-
- /*
- * This is what happens if the INTERCEPT flag is toggled and an
- * interceptor is running:
- *
- * Case 1: clear->set
- * Wait until the server dies (soft reconfig) or
- * terminate the server (hard reconfig)
- *
- * Case 2: set->clear
- * Send a signal to the interceptor to tell it to stop intercepting
- */
- if ( IS_INTERCEPTED( old_conf ) != IS_INTERCEPTED( new_conf ) )
- {
- if ( IS_INTERCEPTED( new_conf ) ) /* case 1 */
- {
- if ( type == RECONFIG_HARD )
- terminate_servers( sp ) ;
- }
- else /* case 2 */
- {
- stop_interception( sp ) ;
- msg( LOG_NOTICE, func, "Stopping interception for %s", sid ) ;
- }
- }
- svc_setup_address_control( sp ) ;
- return( restart_log( sp, old_conf ) ) ;
- }
-
-
-
- /*
- * Stop any logging and restart if necessary.
- * Note that this has the side-effect of using the new common_log
- * handle (in ps.rws.conf) as it should.
- */
- PRIVATE status_e restart_log( sp, old_conf )
- struct service *sp ;
- struct service_config *old_conf ;
- {
- logtype_e old_logtype = LOG( old_conf )->log_type ;
- xlog_h old_log_handle = SDATA( sp )->log_handle ;
- void end_log() ;
- status_e start_log() ;
-
- if ( old_logtype != L_NONE && old_log_handle != NULL )
- end_log( old_logtype, old_log_handle ) ;
-
- return( start_log( sp ) ) ;
- }
-
-
- /*
- * Unregister past versions, register new ones
- * We do it the dumb way: first unregister; then register
- * We try to be a little smart by checking if there has
- * been any change in version numbers (if not, we do nothing).
- * Also, we save the port number
- */
- PRIVATE status_e readjust_rpc_service( old_scp, new_scp )
- register struct service_config *old_scp ;
- register struct service_config *new_scp ;
- {
- unsigned long vers ;
- u_short port = old_scp->port ;
- struct rpc_data *new_rdp = &new_scp->rd ;
- struct rpc_data *old_rdp = &old_scp->rd ;
- unsigned registered_versions = 0 ;
- char *func = "readjust_rpc_service" ;
-
- #ifndef NO_RPC
- new_scp->port = old_scp->port ;
-
- if ( old_rdp->min_version == new_rdp->min_version &&
- old_rdp->max_version == new_rdp->max_version )
- return( OK ) ;
-
- for ( vers = old_rdp->min_version ; vers <= old_rdp->max_version ; vers++ )
- (void) pmap_unset( old_rdp->program_number, vers ) ;
-
- for ( vers = new_rdp->min_version ; vers <= new_rdp->max_version ; vers++ )
- if ( pmap_set( new_rdp->program_number,
- vers, new_scp->protocol.value, port ) )
- registered_versions++ ;
- else
- msg( LOG_ERR, func,
- "pmap_set failed. service=%s, program=%ld, version = %ld",
- new_scp->id, new_rdp->program_number, vers ) ;
- /*
- * If we didn't manage to register any versions, we will
- * have to deactivate the service
- */
- if ( registered_versions == 0 )
- {
- msg( LOG_ERR, func,
- "No versions registered for RPC service %s", new_scp->id ) ;
- /*
- * Avoid the pmap_unset
- */
- new_rdp->min_version = new_rdp->max_version + 1 ;
- return( FAILED ) ;
- }
- #endif /* ! NO_RPC */
- return( OK ) ;
- }
-
-